JgfGraph   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 182
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 61
dl 0
loc 182
rs 10
c 0
b 0
f 0
wmc 30

17 Functions

Rating   Name   Duplication   Size   Complexity  
A metadata 0 4 1
A addNode 0 7 2
A edges 0 3 1
A addEdge 0 4 1
A nodes 0 3 1
A _findNodeById 0 8 3
A addNodes 0 5 2
A removeNode 0 7 3
A getNodeById 0 3 1
A getEdgesByNodes 0 7 2
A _nodeExistsById 0 5 2
A removeEdge 0 3 2
A constructor 0 9 1
A _nodeExists 0 3 1
A addEdges 0 5 2
A _guardAgainstNonExistentNodes 0 9 3
A graphDimensions 0 6 1
1
const check = require('check-types');
2
const _ = require('lodash');
3
const { JgfEdge } = require('./jgfEdge');
4
const { Guard } = require('./guard');
5
6
/**
7
 * An graph object represents the full graph and contains all nodes and edges that the graph consists of.
8
 */
9
class JgfGraph {
10
11
    /**
12
     * Constructor
13
     * @param {string} type Graph classification.
14
     * @param {string} label A text display for the graph.
15
     * @param {boolean} directed Pass true for a directed graph, false for an undirected graph.
16
     * @param {object|null} metadata Custom graph metadata.
17
     */
18
    constructor(type = '', label = '', directed = true, metadata = null) {
19
        this._nodes = [];
20
        this._edges = [];
21
22
        this.type = type;
23
        this.label = label;
24
        this.directed = directed;
25
        this._metadata = metadata;
26
    }
27
28
    /**
29
     * @param {string} nodeId Node to be found.
30
     * @private
31
     */
32
    _findNodeById(nodeId) {
33
        let foundNode = _.find(this._nodes, (existingNode) => existingNode.id === nodeId);
34
        if (!foundNode) {
35
            throw new Error(`A node does not exist with id = ${nodeId}`);
36
        }
37
38
        return foundNode;
39
    }
40
41
    /**
42
     * @param {JgfNode} node Node to be found.
43
     * @private
44
     */
45
    _nodeExists(node) {
46
        return this._nodeExistsById(node.id);
47
    }
48
49
    /**
50
     * @param {string} nodeId Node to be found.
51
     * @private
52
     */
53
    _nodeExistsById(nodeId) {
54
        let foundNode = _.find(this._nodes, (existingNode) => existingNode.id === nodeId);
55
56
        return Boolean(foundNode);
57
    }
58
59
    /**
60
     * Sets the graph meta data.
61
     */
62
    set metadata(value) {
63
        Guard.assertValidMetadataOrNull(value);
64
        this._metadata = value;
65
    }
66
67
    /**
68
     * Returns the graph meta data.
69
     */
70
    get metadata() {
71
        return this._metadata;
72
    }
73
74
    /**
75
     * Returns all nodes.
76
     */
77
    get nodes() {
78
        return this._nodes;
79
    }
80
81
    /**
82
     * Returns all edges.
83
     */
84
    get edges() {
85
        return this._edges;
86
    }
87
88
    /**
89
     * Adds a node to the graph.
90
     * @param {JgfNode} node Node to be added.
91
     * @throws Error if the node already exists.
92
     */
93
    addNode(node) {
94
        if (this._nodeExists(node)) {
95
            throw new Error(`A node already exists with id = ${node.id}`);
96
        }
97
98
        this._nodes.push(node);
99
    }
100
101
102
    /**
103
     * Adds multiple nodes to the graph.
104
     * @param {JgfNode[]} nodes A collection of Jgf node objects to be added.
105
     * @throws Error if one of nodes already exists.
106
     */
107
    addNodes(nodes) {
108
        for (let node of nodes) {
109
            this.addNode(node);
110
        }
111
    }
112
113
    /**
114
     * Removes an existing node from the graph.
115
     * @param {JgfNode} node Node to be removed.
116
     */
117
    removeNode(node) {
118
        if (!this._nodeExists(node)) {
119
            throw new Error(`A node does not exist with id = ${node.id}`);
120
        }
121
122
        _.remove(this._nodes, (existingNode) => existingNode.id === node.id);
123
    }
124
125
    /**
126
     * Get a node by a node ID.
127
     * @param {string} nodeId Unique node ID.
128
     */
129
    getNodeById(nodeId) {
130
        return this._findNodeById(nodeId);
131
    }
132
133
    /**
134
     * Adds an edge to the graph.
135
     * @param {JgfEdge} edge The edge to be added.
136
     */
137
    addEdge(edge) {
138
        this._guardAgainstNonExistentNodes(edge.source, edge.target);
139
        this._edges.push(edge);
140
    }
141
142
    _guardAgainstNonExistentNodes(source, target) {
143
        if (!this._nodeExistsById(source)) {
144
            throw new Error(`addEdge failed: source node isn't found in nodes. source = ${source}`);
145
        }
146
147
        if (!this._nodeExistsById(target)) {
148
            throw new Error(`addEdge failed: target node isn't found in nodes. target = ${target}`);
149
        }
150
    }
151
152
    /**
153
     * Adds multiple edges to the graph.
154
     * @param {JgfEdge[]} edges A collection of Jgf edge objects to be added.
155
     */
156
    addEdges(edges) {
157
        for (let edge of edges) {
158
            this.addEdge(edge);
159
        }
160
    }
161
162
    /**
163
     * Removes existing edge from the graph.
164
     * @param {JgfEdge} edge Edge to be removed.
165
     */
166
    removeEdge(edge) {
167
        _.remove(this._edges, (existingEdge) => existingEdge.isEqualTo(edge, true));
168
    }
169
170
    /**
171
     * Get edges between source node and target node, with an optional edge relation.
172
     * @param {string} source Source node ID.
173
     * @param {string} target Target node ID.
174
     * @param {string|null} relation If passed, only edges having this relation will be returned.
175
     */
176
    getEdgesByNodes(source, target, relation = null) {
177
        this._guardAgainstNonExistentNodes(source, target);
178
179
        let edge = new JgfEdge(source, target, relation);
180
181
        return _.filter(this._edges, (existingEdge) => existingEdge.isEqualTo(edge, check.assigned(relation)));
182
    }
183
184
    get graphDimensions() {
185
        return {
186
            nodes: this._nodes.length,
187
            edges: this._edges.length,
188
        };
189
    }
190
}
191
192
module.exports = {
193
    JgfGraph,
194
};